/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package fr.ove.openmath.mfd2;

/**
 *
 * @author Mr.Sam
 */
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.net.*;
import fr.ove.openmath.mfd2.*;
import fr.ove.openmath.mfd2.events.*;
import fr.ove.openmath.mfd2.frames.*;
import fr.inria.openmath.omapi.*;
import fr.ove.openmath.exceptions.*;
import fr.ove.errordialog.*;

/**
* The main panel of the application Mfd2.
*
* 
*/
public class PanelMfd2 extends Panel implements PanelJomesListener {
    // Le panel contenu tous les menus et assimilés
	Panel panelMenu;
	
	// Le panel contenant tout ce qui va concerner la commande lazy de la bd
	Panel panelLazy;
	Button setLazyButton;
	Label labelLazy;
	Panel panelRadio;
	CheckboxGroup lazyGroup;
	Checkbox radioOn;
	Checkbox radioOff;
	
	Button clearButton;
	
	// Le panel contenant tout ce qui va concerner la commande de la déduction de la bd
	Panel panelDeduction;
	Panel panelDepth;
	Label labelDepth;
	TextField entryDepth;
	Panel panelHeight;
	Label labelHeight;
	TextField entryHeight;
	
	// Le panel principal concernant les requêtes à la bd
	Panel panelMain;
    Mfd2ScrollPane mfd2ScrollPane;
	Panel panelRequest;
	TextField entryRequest;
	Panel panelButton;
	Button buttonRequest;
	Button buttonContaining;
	Button buttonClear;
	
	// Dernier élément de l'interface, une fenêtre de status
	TextArea statusArea;
	//StatusArea statusArea;
    
    // La liste des Requêtes
    Vector listRequests = new Vector();
    // La requête courante
    PanelJomes currentRequest;
    
    // Les fenêtre OpenMath
    ShowOM frameRequest;
    ShowOM frameResult;
    
    RequestContaining frameContaining;
    boolean isContainingRequest = false;
    
    boolean updateEntryRequest = true;
    
    // La communication
	private InputStream inputStream;
	private OutputStream outputStream;
	
	private Selector selector;
    
    /**
    * The constructor
    */
	public PanelMfd2(Socket socket) {
	    try {
        	outputStream = socket.getOutputStream();
        	inputStream = socket.getInputStream();
	    }
	    catch (IOException ioe) {
	        System.out.println("Can't get streams from socket");
	        ioe.printStackTrace();
	    }
	    
		Font commonFont = new Font("Dialog", Font.PLAIN, 14);
		Color commonColor = Color.lightGray;
		
		setLayout(new BorderLayout(0,0));
		setBackground(commonColor);
		
		panelMenu = new Panel();
		panelMenu.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 2));
		panelMenu.setBackground(commonColor);
		add("North", panelMenu);
		
		// La barre de menu
        MenuBar menuBar = new MenuBar();
        
        // Premier menu : chargement des livres
        Menu menu = new Menu("Load Book");
        
        // Qui va traiter les événements du type ActionEvent
        MenuLoadHandler menuLoadHandler = new MenuLoadHandler();
        
        // On met dans ce premier menu tous les items nécessaires (i.e. les livres à charger)
        MenuItem menuItem = new MenuItem("base.fdb");
		menuItem.setActionCommand(menuItem.getLabel());
		menuItem.addActionListener(menuLoadHandler);
        menu.add(menuItem);
        
        menuItem = new MenuItem("basic.fdb");
		menuItem.setActionCommand(menuItem.getLabel());
		menuItem.addActionListener(menuLoadHandler);
        menu.add(menuItem);
		
        menuItem = new MenuItem("even.fdb");
		menuItem.setActionCommand(menuItem.getLabel());
		menuItem.addActionListener(menuLoadHandler);
        menu.add(menuItem);
		
        menuItem = new MenuItem("trigo.fdb");
		menuItem.setActionCommand(menuItem.getLabel());
		menuItem.addActionListener(menuLoadHandler);
        menu.add(menuItem);
        
        menuItem = new MenuItem("AS");
		menuItem.setActionCommand(menuItem.getLabel());
		menuItem.addActionListener(menuLoadHandler);
        menu.add(menuItem);
        
        // On met ce menu dans la barre de menus
        menuBar.add(menu);
        
        // Second menu : vision OpenMath de la chose
        menu = new Menu("OpenMath");
        
        // Qui va traiter les événements du type ActionEvent
        MenuOpenMathHandler menuOpenMathHandler = new MenuOpenMathHandler();

        // On met dans ce premier menu tous les items nécessaires
        menuItem = new MenuItem("Request");
		menuItem.setActionCommand(menuItem.getLabel());
		menuItem.addActionListener(menuOpenMathHandler);
        menu.add(menuItem);
        
        menuItem = new MenuItem("Result");
		menuItem.setActionCommand(menuItem.getLabel());
		menuItem.addActionListener(menuOpenMathHandler);
        menu.add(menuItem);
        
        // On met ce menu dans la barre de menus
        menuBar.add(menu);
        
        // Le panel spécial pour pouvoir mettre des barres de menu dans des applets.
        MenuPanel menuPanel = new MenuPanel();
		menuPanel.setFont(commonFont);
		// Comme les menus déroulants sont en fait des Popup Menus et qu'on ne peut pas changer
		// leur couleur (ni quoi que ce soit d'ailleurs) on met la barre de menu à la même couleur
		// que ces derniers
        menuPanel.setBackground(commonColor);
        menuPanel.setMenuBar(menuBar);
        menuPanel.setBounds(0, 0, 470, 30);
        
        // On ajoute la barre de menu
        panelMenu.add(menuPanel);
		
    	setLazyButton = new Button("Lazy OFF");
    	setLazyButton.setFont(commonFont);
    	setLazyButton.setBackground(commonColor);
    	
    	setLazyButton.addActionListener(
    	    new ActionListener () {
        		public void actionPerformed(ActionEvent event) {
        		    String currentLabel = setLazyButton.getLabel();
        		    boolean lazy;
        		    if (currentLabel.equals("Lazy ON")) {
                		setLazyButton.setLabel("Lazy OFF");
                		lazy = false;
                	}
                    else {
                		setLazyButton.setLabel("Lazy ON");
                		lazy = true;
                	}
                	
		            statusArea.append("> set lazy " + lazy + " sent\n");
                    String omObject = "<OMOBJ>\n  <OMA>\n    <OMS cd=\"mfd2\" name=\"setlazy\"/>\n    <OMS cd=\"logic\" name=\"" + lazy + "\"/>\n  </OMA>\n</OMOBJ>";
                    setObject(omObject.getBytes());
            	}
    	    }
    	);
		
		panelMenu.add(setLazyButton);
		
		clearButton = new Button("Clear");
    	clearButton.setFont(commonFont);
    	clearButton.setBackground(commonColor);
		
    	clearButton.addActionListener(
    	    new ActionListener () {
        		public void actionPerformed(ActionEvent event) {
                    statusArea.append("> clear sent\n");
                    String omObject = "<OMOBJ>\n  <OMS cd=\"mfd2\" name=\"clear\"/>\n</OMOBJ>";
                    setObject(omObject.getBytes());
            	}
    	    }
    	);
		
		panelMenu.add(clearButton);
				
		panelDeduction = new Panel();
		panelDeduction.setLayout(new FlowLayout(FlowLayout.CENTER,10,0));
		panelMenu.add(panelDeduction);
		
		panelDepth = new Panel();
		panelDepth.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
		panelDeduction.add(panelDepth);
		
		labelDepth = new Label("Depth");
		labelDepth.setFont(commonFont);
		panelDepth.add(labelDepth);
		
		entryDepth = new TextField();
		entryDepth.setFont(commonFont);
		entryDepth.setText("1");
		panelDepth.add(entryDepth);
		
        // On tape sur la touche entrée (on envoie le paramètre de déduction)
		entryDepth.addActionListener(
		    new ActionListener() {
		        public void actionPerformed(ActionEvent event) {
		            statusArea.append("> deduction depth sent : " + entryDepth.getText() + "\n");
                    String omObject = "<OMOBJ>\n  <OMA>\n    <OMS cd=\"mfd2\" name=\"setdepth\"/>\n    <OMI> " + entryDepth.getText() + " </OMI>\n  </OMA>\n</OMOBJ>";
                    setObject(omObject.getBytes());
                }
            }
        );

		panelHeight = new Panel();
		panelHeight.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
		panelDeduction.add(panelHeight);
		
		labelHeight = new Label("Height");
		labelHeight.setFont(commonFont);
		panelHeight.add(labelHeight);
		
		entryHeight = new TextField();
		entryHeight.setFont(commonFont);
		entryHeight.setText("2");
		panelHeight.add(entryHeight);
		
        // On tape sur la touche entrée (on envoie le paramètre de déduction)
		entryHeight.addActionListener(
		    new ActionListener() {
		        public void actionPerformed(ActionEvent event) {
		            statusArea.append("> deduction height sent : " + entryHeight.getText() + "\n");
                    String omObject = "<OMOBJ>\n  <OMA>\n    <OMS cd=\"mfd2\" name=\"setheight\"/>\n    <OMI> " + entryHeight.getText() + " </OMI>\n  </OMA>\n</OMOBJ>";
                    //System.out.println("On envoie height :\n " + omObject);
                    setObject(omObject.getBytes());
                }
            }
        );

		panelMain = new Panel();
		panelMain.setLayout(new BorderLayout(0,0));
		add("Center", panelMain);
		
		
        // Le panneau où vont se trouver les différentes requêtes et leur réponse respective
        mfd2ScrollPane = new Mfd2ScrollPane(460, 300, 5);
        mfd2ScrollPane.setBackground(Color.white);
        
        // La première requête (qui est donc la requête courante)
        currentRequest = new PanelJomes();
        currentRequest.addPanelJomesListener(this);
        
        mfd2ScrollPane.add(currentRequest);
        // On l'ajoute à la liste des requêtes
        listRequests.addElement(currentRequest);
        
        // On ajoute les requête au panel
        panelMain.add("Center", mfd2ScrollPane);
		
		panelRequest = new Panel();
		panelRequest.setLayout(new BorderLayout(0,0));
		panelMain.add("South", panelRequest);
		
		entryRequest = new TextField();
		entryRequest.setFont(new Font("Dialog", Font.BOLD, 14)/*commonFont*/);
		entryRequest.requestFocus();
		panelRequest.add("Center", entryRequest);
		
		// Le traitement des actions sur la zone de saisie
		// On saisi du texte
		entryRequest.addTextListener(
        	new TextListener() {
        	    public void textValueChanged(TextEvent event) {
        	        if (!entryRequest.getText().equals("")) {
            	        updateEntryRequest = false;
            			currentRequest.setLinearRequest(entryRequest.getText());
            	        updateEntryRequest = true;
            		}
        		}
        	}
        );
        
        // On tape sur la touche entrée (on envoie la requête)
		entryRequest.addActionListener(
		    new ActionListener() {
		        public void actionPerformed(ActionEvent event) {
		            byte[] omRequest = currentRequest.getOpenMathRequest().getBytes();
		            if (omRequest.length > 0) {
    		            statusArea.append("> Request sent\n");
    		            setObject(omRequest);
    		            
    		            /*
    		            // A revoir parce que ça ça veut dire que l'on change de requête courante avant
    		            // d'avoir, a priori, reçu la réponse à la requête.
    		            // En attendant, on fait.....
    		            
    		            // Si la requête courante est la dernière de la liste, alors on créé une nouvelle
    		            // requête. Sinon, on met le requête suivante comme requête courante.
    		            int currentRequestIndex = listRequests.indexOf(currentRequest);
    		            if (currentRequestIndex == (listRequests.size() -1))
        		            // Création d'un nouvelle requête
        		            createNewRequest();
        		        else {
        		            currentRequest = (PanelJomes) listRequests.elementAt(currentRequestIndex + 1);
        		            
                            // On envoie un événement comme quoi il faut les mettre à jour
                    	    PanelJomesEvent panelJomesEvent = new PanelJomesEvent(currentRequest);
                    	    panelJomesEvent.setAction(PanelJomesEvent.UPDATE, null);
                    	    currentRequest.firePanelJomesEvent(panelJomesEvent);
                    	    
                            // On envoie un événement comme quoi il faut mettre à jour qui est sélectionné
                    	    panelJomesEvent.setAction(PanelJomesEvent.UPDATE_SELECTION, null);
                    	    currentRequest.firePanelJomesEvent(panelJomesEvent);
                    	    
                    	    // Le focus sur la saisie de la requête
                    	    entryRequest.requestFocus();
        		        }
        		        */
                    }
                }
            }
        );

		
		panelButton = new Panel();
		panelButton.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
        panelButton.setFont(commonFont);
		panelButton.setBackground(commonColor);
		panelRequest.add("East", panelButton);
		
		buttonRequest = new Button();
		buttonRequest.setLabel("New");
		buttonRequest.setFont(commonFont);
		buttonRequest.setBackground(commonColor);
		panelButton.add(buttonRequest);
		
		// Le traitement des actions sur le bouton
		buttonRequest.addActionListener(
		    new ActionListener() {
		        public void actionPerformed(ActionEvent event) {
		            // Création d'un nouvelle requête
		            createNewRequest();
                }
            }
        );

		buttonContaining = new Button();
		buttonContaining.setLabel("Find");
		buttonContaining.setFont(commonFont);
		buttonContaining.setBackground(commonColor);
		panelButton.add(buttonContaining);
		
		frameContaining = new RequestContaining((Frame) getParent(), this);
		frameContaining.setVisible(false);
		
		// Le traitement des actions sur le bouton
		buttonContaining.addActionListener(
		    new ActionListener() {
		        public void actionPerformed(ActionEvent event) {
		            if (frameContaining.isVisible())
		                frameContaining.setVisible(false);
		            else
		                frameContaining.setVisible(true);
                }
            }
        );

		buttonClear = new Button();
		buttonClear.setLabel("Clear");
		buttonClear.setFont(commonFont);
		buttonClear.setBackground(commonColor);
		panelButton.add(buttonClear);
		
		// Le traitement des actions sur le bouton
		buttonClear.addActionListener(
		    new ActionListener() {
		        public void actionPerformed(ActionEvent event) {
                    mfd2ScrollPane.removeAll();
                    listRequests.setSize(0);
                    
                    createNewRequest();
                }
            }
        );

		statusArea = new TextArea(5, 2);
		statusArea.setBounds(0,402,734,150);
		statusArea.setFont(new Font("Dialog", Font.PLAIN, 10));
		add("South", statusArea);
		
		selector = new Selector(this, currentRequest);
		selector.start();
	}
	
	public void createNewRequest() {
        currentRequest = new PanelJomes();
        mfd2ScrollPane.add(currentRequest);
        
        selector.setOwner(currentRequest);
        
        currentRequest.addPanelJomesListener(this);

        // On l'ajoute à la liste des requêtes
        listRequests.addElement(currentRequest);
        
        // On associe les listeners potentiel de currentRequest
        if (frameRequest != null)
            currentRequest.addPanelJomesListener(frameRequest);
            
        if (frameResult != null)
            currentRequest.addPanelJomesListener(frameResult);
        
	    // Initialisation de la zone de saisie de la requête
	    entryRequest.requestFocus();
	    
        // On envoie un événement comme quoi il faut les mettre à jour
	    PanelJomesEvent panelJomesEvent = new PanelJomesEvent(currentRequest);
	    panelJomesEvent.setAction(PanelJomesEvent.UPDATE, null);
	    currentRequest.firePanelJomesEvent(panelJomesEvent);
	    
	    //currentRequest.setSelected(true);
        // On envoie un événement comme quoi il faut mettre à jour qui est sélectionné
	    panelJomesEvent.setAction(PanelJomesEvent.UPDATE_SELECTION, null);
	    currentRequest.firePanelJomesEvent(panelJomesEvent);
    }
    
	public void setIsConstainingRequest(boolean isContainingRequest) {
	    this.isContainingRequest = isContainingRequest;
	}
	
    /******************************************************
     *                                                    *
     *  Implémentation de l'interface PanelJomesListener  *
     *                                                    *
     ******************************************************/

    public void consumePanelJomesEvent(PanelJomesEvent panelJomesEvent) {
        currentRequest = (PanelJomes) panelJomesEvent.getSource();
        selector.setOwner(currentRequest);
        
        int action = panelJomesEvent.getAction();
        switch (action) {
            case PanelJomesEvent.UPDATE_REQUEST :
            case PanelJomesEvent.UPDATE :
                if (updateEntryRequest) {
                    entryRequest.setText(currentRequest.getLinearRequest());
                    entryRequest.setCaretPosition(entryRequest.getText().length());
                }
            case PanelJomesEvent.UPDATE_RESULT :
                validate();
                break;
            case PanelJomesEvent.UPDATE_SELECTION :
                for (Enumeration e = listRequests.elements(); e.hasMoreElements(); )
                    ((PanelJomes) e.nextElement()).setSelected(false);
                    
                currentRequest.setSelected(true);
        }
    }
    	
	public InputStream getInputStream() {
	    return inputStream;
	}
	
	public void setStatusMessage(String mesg) {
	    statusArea.append(mesg);
	}
    
    /**
     * Setter method for the  object property.
     * Wraps the object into an <CODE>CommEvent</CODE> and fires it off to the
     * communication module(s)
     * @param obj the " object" to be communicated
     */
    public void setObject(byte[] obj) {
        try {
        	outputStream.write(obj);
        }
        catch (IOException ioe) {
            System.out.println("Can't write into the socket");
            ioe.printStackTrace();
        }
    }
    
    public void closeStreams() {
        try {
            outputStream.close();
            inputStream.close();
        }
        catch (IOException ioe) {
            System.out.println("Can't close IO-streams");
            ioe.printStackTrace();
        }
    }
    
    public Selector getSelector() {
        return selector;
    }
    
    public PanelJomes getCurrentRequest() {
        return currentRequest;
    }    
	
	// Les ActionListener pour les menus

    private class MenuLoadHandler implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            String action = e.getActionCommand();
            statusArea.append("> load " + action + "\n");
            
            String omObject = "<OMOBJ>\n  <OMA>\n    <OMS cd=\"mfd2\" name=\"load\"/>\n    <OMSTR>" + action + "</OMSTR>\n  </OMA>\n</OMOBJ>";
            setObject(omObject.getBytes());
        }
    }
    
    private class MenuOpenMathHandler implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            String action = e.getActionCommand();
            int panelJomesAction;
            
            ShowOM frame;
            
            if (action.equals("Request")) {
                panelJomesAction = PanelJomesEvent.UPDATE_REQUEST;
                if (frameRequest != null)
                    frame = frameRequest;
                else {
                    frame = new ShowOM("Request");
                    frameRequest = frame;
            		frameContaining.addRequestContainingListener(frame);
                }
            }
            else {
                panelJomesAction = PanelJomesEvent.UPDATE_RESULT;
                if (frameResult != null)
                    frame = frameResult;
                else {
                    frame = new ShowOM("Result");
                    frameResult = frame;
            		frameContaining.addRequestContainingListener(frame);
                }
            }
            
            
            frame.show();
            Component comps[] = mfd2ScrollPane.getAll();
            int count = comps.length;
            PanelJomes pj;
            for (int i = 0; i < count; i++) {
                pj = (PanelJomes) comps[i];
                pj.removeAllPanelJomesListener();
                
                if (frameRequest != null)
                    pj.addPanelJomesListener(frameRequest);
                    
                if (frameResult != null)
                    pj.addPanelJomesListener(frameResult);
                    
                pj.addPanelJomesListener(PanelMfd2.this);
            }
            
            // On envoie un événement comme quoi il faut les mettre à jour
    	    PanelJomesEvent panelJomesEvent = new PanelJomesEvent(currentRequest);
    	    panelJomesEvent.setAction(panelJomesAction, null);
    	    currentRequest.firePanelJomesEvent(panelJomesEvent);
            
        }
    }
}
